﻿namespace GoodAdmin.System;

/// <inheritdoc cref="ISysUserService"/> 
public class SysUserService:DbRepository<SysUser>, ISysUserService, ITransient
{
    private readonly IConfigService _configService;
    private readonly IRedisCacheManager _redisManager;
    private readonly IRelationService _relationService;
    private readonly ISysRoleService _roleService;
    private readonly IMenuService _menuService;
    private readonly ISysOrgService _orgService;
    private readonly IFileService _fileService;
    private readonly ISysImportErrorListService _importErrorListService;

    public SysUserService( IConfigService configService, IRedisCacheManager redisManager, 
        IRelationService relationService, ISysRoleService roleService,
        IMenuService menuService,ISysOrgService orgService,IFileService fileService,
        ISysImportErrorListService importErrorListService)
	{
        _configService = configService;
        _redisManager = redisManager;
        _relationService = relationService;
        _roleService = roleService;
        _menuService = menuService;
        _orgService = orgService;
        _fileService = fileService;
        _importErrorListService = importErrorListService;
    }

    /// <inheritdoc/>
    public async Task AddUser(UserAddInput userInput)
    {
        await CheckInput(userInput);

        //设置默认密码
        var pwd= await GetDefaultPassword();
        userInput.Password = pwd;
        //插入时忽略系统标识字段
        await Context.Insertable<SysUser>(userInput)
            .IgnoreColumns(u=>new { u.SystemMark }).ExecuteCommandAsync();
    }
   
    /// <inheritdoc/>
    public async Task UpdateUser(UserUpdateInput userInput)
    {
        await CheckInput(userInput);
        var user= await GetUserById(userInput.Id);
        var res = await Context.Updateable<SysUser> (userInput)
            .IgnoreColumns(u => new { u.SystemMark, u.Password, u.Status }).ExecuteCommandAsync();
        if (res > 0)
        {
            //删除用户缓存
            DeleteUserCache(userInput.Id);
            //如果变更了组织，那么也需要移除当前的数据范围
            if (user.OrgId != userInput.OrgId)
            {
                RemoveDataScopeCache(userInput.Id);
            }
        }
            
    }

    /// <inheritdoc/>
    public async Task DeleteUser(List<BaseIdInput> Input)
    {
        var ids = Input.Select(x => x.Id).ToList();
        if (ids.Count == 0) return;
        //不能删除自己
        if (ids.Contains(UserManager.UserId))
            throw Oops.Bah(UserErrorCode.U003);

        var userList = await GetListAsync(u => ids.Contains(u.Id));
        //不能删除系统自带超级管理员
        if(userList.Any(u=> u.Account==RoleConst.SUPERADMIN &&ids.Contains(u.Id)))
        {
            throw Oops.Bah(UserErrorCode.U002);
        }
        
        await DeleteByIdsAsync(ids.Cast<dynamic>().ToArray());
        //删除用户缓存
        DeleteUserCache(ids);
    }

    /// <inheritdoc/>
    public async Task<bool> DisableUser(BaseIdInput input)
    {
        var user = await GetUserById(input.Id);        
        if (user.Account == RoleConst.SUPERADMIN)
        {
            throw Oops.Bah(UserErrorCode.U004);
        }
        if (input.Id == UserManager.UserId)
        {
            throw Oops.Bah(UserErrorCode.U005);
        }

        var res = await UpdateAsync(u => new SysUser { Status = CommonConst.STATUS_DISABLE }, u => u.Id == input.Id);
        if (res)
        {
            DeleteUserCache(input.Id);
        }
        return res;   
    }

    /// <inheritdoc/>
    public async Task<bool> EnableUser(BaseIdInput input)
    {
        var res = await UpdateAsync(u => new SysUser { Status = CommonConst.STATUS_ENABLE }, u => u.Id == input.Id);
        if (res)
        {
            DeleteUserCache(input.Id);
        }
        return res;
    }

    /// <inheritdoc/>
    public async Task<SqlSugarPagedList<SysUser>> GetUserPage(UserPageInput input)
    {       
        return await Context.Queryable<SysUser>()
            .LeftJoin<SysOrg>((u,o)=>u.OrgId==o.Id)
            .LeftJoin<SysPosition>((u,o,p)=>u.PosId==p.Id)
            .WhereIF(input.Expression!=null,input.Expression?.ToExpression())
            .WhereIF(input.OrgId>0, u => u.OrgId == input.OrgId)
            .WhereIF(!string.IsNullOrWhiteSpace(input.Gender), u => u.Gender == input.Gender)
            .WhereIF(!string.IsNullOrWhiteSpace(input.Status), u => u.Status == input.Status)
            .WhereIF(!string.IsNullOrWhiteSpace(input.Name), u => u.Name.Contains(input.Name))
            .Select((u,o,p)=>new SysUser { Id=u.Id.SelectAll(),OrgName=o.OrgName,PosName=p.Name})
            .Mapper(u => u.Password=null)
            .ToPagedListAsync(input.PageIndex, input.PageSize);
    }

    /// <inheritdoc/>
    public async Task<SysUser> GetUserById(long userId)
    {
        var user = _redisManager.HashGet<SysUser>(CacheConst.SysUser, userId.ToString() )[0];
        if (user == null)
        {
            user = await GetFirstAsync(u => u.Id == userId);
            if(user!=null)
                _redisManager.HashSet(CacheConst.SysUser, userId.ToString(), user);
        }        
        return user;
    }

    /// <inheritdoc/>
    public async Task<UserOutput> GetLoginUserDetail()
    {
        var user = await GetUserById(UserManager.UserId);
        var menuList= await GetUserRoleMenu(UserManager.UserId);
        var buttons = menuList.Where(it=>it.MenuType== CommonConst.MENUTYPE_BUTTON).Select(it => it.Path).ToList();
        var userOutput=user.Adapt<UserOutput>();
        userOutput.Buttons= buttons;
        return userOutput;
    }

    /// <inheritdoc/>
    public async Task<SysUser> GetUserByAccount(string Account)
    {
        var key = CacheConst.SysUserAccount;
        var userId = _redisManager.HashGet<long>(key, Account)[0];
        if (userId == 0)
        {
            userId = await GetFieldFirstAsync(u => u.Account == Account, u => u.Id);
            //如果没找到，也存下来，减少短时间内续数据库开销
            _redisManager.HashSet(key, Account, userId,1);            
        }
        if (userId > 0)
        {
            return await GetUserById(userId);
        }
        return null;
    }

    /// <inheritdoc/>
    public async Task<List<MenuOutPut>> GetLoginUserMenu()
    {
        //获取用户角色对应的菜单
        var menuList = await GetUserRoleMenu(UserManager.UserId);
        menuList.ForEach(it => it.Meta = new MenuMeta { Title = it.Title, IsKeepAlive = it.IsKeepAlive, Icon = it.Icon, IsLink = it.IsLink });
        //构造成树形结构
        menuList = _menuService.ConstructMenuTree(menuList.Where(it => it.MenuType != CommonConst.MENUTYPE_BUTTON).ToList());
        var menuOutput=menuList.Adapt<List<MenuOutPut>>();
        return menuOutput;
    }
    
    
    /// <inheritdoc/>
    public async Task<List<RoleSelectorOutput>> GetUserRoleList(BaseIdInput input)
    {       
        var res = await _roleService.GetUserRoleList(input.Id);
        return res.Adapt<List<RoleSelectorOutput>>();
    }

    /// <inheritdoc/>
    public async Task GrantUserRole(UserRoleInput input)
    {
        //不能为系统内置超级管理员赋予角色
        var user = await GetUserById(input.Id);
        if (user == null) return;
        
        if (user.Account == RoleConst.SUPERADMIN)
        {
            throw Oops.Bah(RoleErrorCode.R005);
        }
        var res = await Context.Ado.UseTranAsync(async () =>
        {
            await _relationService.DeleteRelationByCategoryAndTargetId(CategoryConst.RELATION_ROLE_USER, input.Id);
            await _relationService.AddRelationByTargetId(CategoryConst.RELATION_ROLE_USER, input.Id, input.RoleIdList);
        });
        //成功之后再刷新缓存
        if (res.IsSuccess)
        {
            DeleteUserCache(input.Id);
            //删除用户数据范围
            RemoveDataScopeCache(input.Id);
        }
    }

    /// <inheritdoc/>
    public async Task ResetPassword(BaseIdInput input)
    {
        var pwd = await GetDefaultPassword();
        if(await UpdateAsync(u => new SysUser { Password = pwd }, u => u.Id == input.Id))
        //删除用户缓存
            DeleteUserCache(input.Id);
    }

    /// <inheritdoc/>
    public async Task ChangePassword(ChangePwdInput input)
    {
        var passwordNew = CryptogramUtil.Sm2Encrypt(input.PasswordNew);
        await Context.Updateable<SysUser>().SetColumns(u => u.Password==passwordNew).Where(u=>u.Id==input.Id).ExecuteCommandAsync();
    }

    /// <inheritdoc/>
    public async Task<string> GetDefaultPassword()
    {
        var defaultPwd =  (await _configService.GetConfigInfo(CategoryConst.SYS_CONFIG, ConfigConst.SYS_DEFAULT_PASSWORD)).ConfigValue;
        //使用sm2加密
        defaultPwd = CryptogramUtil.Sm2Encrypt(defaultPwd);
        return defaultPwd;
    }

    /// <inheritdoc/>
    public async Task<List<UserSimpleOutput>> GetUserList(List<long> UserIdList)
    {
        var users = await GetFieldListAsync(u => UserIdList.Contains(u.Id),
            u => new UserSimpleOutput
            {
                Id = u.Id,
                Name = u.Name,
                Account = u.Account,
                OrgId = u.OrgId,
                PosId = u.PosId,
                WorkId = u.WorkId
            });        
        return users;
    }

    /// <inheritdoc/>
    public FileStreamResult TemplateDownload()
    {
        string fileName= "用户信息导入模板.xlsx";

        ExcelConfig excelconfig = new()
        {
            FileName = fileName,
            HeadFont = "微软雅黑",
            HeadPoint = 10,
            IsAllSizeColumn = true,
            ColumnHeaderModel = new List<ExcelColumnHeaderModel>(),
            ColumnModel = new List<ExcelColumnModel>()
        };
        var columnList = UtilMethod.GetEntityMetadataToList<UserBatchImport>();
        foreach(var item in columnList)
        {
            //如果列名是*开头，表示必填，红色字体显示
            excelconfig.ColumnHeaderModel.Add(new ExcelColumnHeaderModel()
            {
                Column = item.FieldName,
                ExcelColumn = item.Comment,
                ForeColor = item.Comment.StartsWith("*") ? IndexedColors.Red : null
            });            
            excelconfig.ColumnModel.Add(new ExcelColumnModel() { Column = item.FieldName, ExcelColumn = item.Comment });
        }
        
        FileStreamResult stream = ExcelExportHelper<UserBatchImport>.Export(new List<UserBatchImport>(),excelconfig, fileName);
        return stream;       
    }

    /// <inheritdoc/>
    public async Task<ExcelImportResult> BatchAddUser(IFormFile file)
    {
        var sysFile = await _fileService.UploadFile(file, FileConst.BatchImport);
        var columnList = UtilMethod.GetEntityMetadataToList<UserBatchImport>();
        var importData = await ExcelImportHelper.ReadExcelToList<UserBatchImport>(Path.Combine(sysFile.EnginePath,sysFile.StoragePath), columnList);
        if (importData.Count == 0)
        {
            throw Oops.Bah(UserErrorCode.U014);
        }
        var (userList,errorList)=await CheckImportData(importData);
        if (userList.Count > 0)
        {
            var res = await Context.Ado.UseTranAsync(async () =>
            {
                //如果数据量大于1000，使用BulkCopy插入,速度更快
                if (userList.Count >= 1000)
                {
                    DbContext.Db.Fastest<SysUser>().PageSize(100000).BulkCopy(userList);
                }
                else
                {
                    await InsertRangeAsync(userList);
                }
                
            });
            //如果插入失败，那么数据存到errorList，并返回给前端
            if (!res.IsSuccess)
            {
                foreach (var item in userList)
                {
                    var errorItem = item.Adapt<UserBatchImportOutput>();
                    errorItem.ErrorMessages = "插入数据库失败";
                    errorList.Add(errorItem);
                }
            }
        }
        var errorCount = errorList.Count;
        var message = $"成功导入{userList.Count}条数据，失败{errorCount}条数据";
        var excelImportResult = new ExcelImportResult() { ErrorCount = errorCount, FileName = "批量用户导入错误记录.xlsx", Message = message, IsHasErrorFile = errorCount > 0 };
        if (errorCount > 0)
        {
            SysImportErrorList importErrorList = new() { ErrorList = errorList.ToJson(), MethodName = nameof(BatchAddUser) };
            excelImportResult.ErrorListId = await _importErrorListService.Save(importErrorList);
        }

        return excelImportResult;
    }

    public async Task<FileStreamResult> ErrorDownload(BaseIdInput input)
    {
        var importErrorList = await _importErrorListService.GetById(input.Id);
        try
        {
            var errorList = importErrorList.ErrorList.ToList<UserBatchImportOutput>();
            var columnList = UtilMethod.GetEntityMetadataToList<UserBatchImportOutput>();
            string fileName = "批量用户导入错误记录.xlsx";
            ExcelConfig excelconfig = new()
            {
                FileName = fileName,
                HeadFont = "微软雅黑",
                HeadPoint = 10,
                IsAllSizeColumn = true,
                ColumnHeaderModel = new List<ExcelColumnHeaderModel>(),
                ColumnModel = new List<ExcelColumnModel>(),
                Title="错误记录"
            };
            foreach (var item in columnList)
            {
                //如果列名是*开头，表示必填，红色字体显示
                excelconfig.ColumnHeaderModel.Add(new ExcelColumnHeaderModel()
                {
                    Column = item.FieldName,
                    ExcelColumn = item.Comment,

                });
                excelconfig.ColumnModel.Add(new ExcelColumnModel() { Column = item.FieldName, ExcelColumn = item.Comment, ForeColor = item.Comment.StartsWith("*") ? IndexedColors.Red : null });
            }
            FileStreamResult stream = ExcelExportHelper<UserBatchImportOutput>.Export(errorList, excelconfig, fileName);
            return stream;
        }
        catch (Exception)
        {

            throw;
        }
        
    }
    #region

    /// <summary>
    /// 检查输入
    /// </summary>
    /// <param name="user"></param>
    /// <returns></returns>
    private async Task CheckInput(SysUser user)
    {
        //检查部门是否存在
        if (!await _orgService.IsOrgExist(user.OrgId))
        {
            throw Oops.Bah(UserErrorCode.U010);
        }

        //判断帐号是否重复
        if (await IsAnyAsync(u => u.Account == user.Account && u.Id !=user.Id ))
        {
            throw Oops.Bah(UserErrorCode.U001);
        }
    }

    /// <summary>
    /// 检查导入数据
    /// </summary>
    /// <param name="importData"></param>
    /// <returns></returns>
    private async Task<(List<SysUser>, List<UserBatchImportOutput>)> CheckImportData(List<UserBatchImport> importData)
    {
        var accountList=importData.Select(u=>u.Account.Trim()).ToList();//导入账号
        var users = await GetFieldListAsync(u => true,
            u => new SysUser
            {
                Id = u.Id,
                Name = u.Name,
                Account = u.Account,
                OrgId = u.OrgId,
                PosId = u.PosId,
                WorkId = u.WorkId,
                Phone = u.Phone
            });
        var sysOrgs = await _orgService.GetOrgList();         
        List<UserBatchImportOutput> errorList = new();
        List<SysUser> userList = new();
        // 检查导入数据中账号是否重复
        var repeatAccountList = importData.GroupBy(u => u.Account).Where(u => u.Count() > 1).Select(u=>u.Key).ToArray();
        // 检查导入数据中手机号是否重复
        var repeatPhoneList = importData.GroupBy(u => u.Phone).Where(u => u.Count() > 1).Select(u => u.Key).ToArray();
        var defaultPwd = await GetDefaultPassword();
        foreach(var item in importData)
        {
            var errorMessage=string.Empty;
            if (repeatAccountList.Contains(item.Account))
            {
                errorMessage += "账号重复;";
            }
            if (users.Any(u => u.Account == item.Account))
            {
                errorMessage += "系统中已有该账号;";
            }
            if (repeatPhoneList.Contains(item.Phone))
            {
                errorMessage += "手机号重复;";
            }
            if (users.Any(u => u.Phone == item.Phone))
            {
                errorMessage += "系统中已有该手机号;";
            }
            var res=item.TryValidate();//通过实体验证
            foreach(var error in res.ValidationResults)
            {
                errorMessage += error.ErrorMessage + ";";
            }
            if (string.IsNullOrWhiteSpace(item.OrgName))
            {
                errorMessage += "组织名称不能为空;";
            }
            else if (!sysOrgs.Any(o => o.LongName.Replace(" ", "") == item.OrgName))
            {
                errorMessage += "系统中不存在该组织;";
            }            
           
            if (!string.IsNullOrEmpty(errorMessage))
            {
                var errorItem= item.Adapt<UserBatchImportOutput>();
                errorItem.ErrorMessages = errorMessage;
                errorList.Add(errorItem);
            }
            else
            {
                var user = item.Adapt<SysUser>();
                user.OrgId = sysOrgs.FirstOrDefault(o => o.LongName == item.OrgName).Id;
                user.Password = defaultPwd;
                userList.Add(user);
            }
        }
        return (userList, errorList);
    }

    /// <summary>
    /// 获取用户角色菜单列表
    /// </summary>
    /// <param name="userId"></param>
    /// <returns></returns>
    private async Task<List<SysMenu>> GetUserRoleMenu(long userId)
    {
        var roleList = await _roleService.GetUserRoleList(userId);
        List<SysMenu> menuList = new();
        List<SysRelation> relationList = new();
        //如果是超管，那么直接获取所有菜单列表
        if (roleList.Any(r => r.Code == RoleConst.SUPERADMIN))
        {
            menuList = await _menuService.GetMenuList();
        }
        else
        {
            if (roleList.Count > 0)
            {
                //角色对应菜单
                relationList = await _relationService.GetRelationListByObjectIdList(CategoryConst.RELATION_ROLE_MENU, roleList.Select(r => r.Id).ToList());
                //菜单信息
                menuList = await _menuService.GetMenuListByMenuId(relationList.Select(r => r.TargetId).Distinct().ToList());
            }            
            var commonRoleMenu = await _roleService.GetCommonRoleMenu();//公共角色菜单
            menuList.AddRange(commonRoleMenu);
            menuList=menuList.OrderBy(it => it.SortCode).ToList();
        }
        return  menuList;
    }

    /// <summary>
    /// 删除用户缓存
    /// </summary>
    /// <param name="userId"></param>
    private void DeleteUserCache(long userId)
    {
        _redisManager.HashDel<SysUser>(CacheConst.SysUser, userId.ToString());
    }

    private void DeleteUserCache(List<long> userIdList)
    {
        var idArray = userIdList.Select(it => it.ToString()).ToArray();
        _redisManager.HashDel<SysUser>(CacheConst.SysUser, idArray);
    }

    private void RemoveDataScopeCache(long userId)
    {
        _redisManager.Remove(CacheConst.DataScope+ userId);
    }
    #endregion
}